﻿//
// Created by 25007 on 2022/11/15.
//

#ifndef FFMPEG_PLAYER_INCLUDE_LOCAL_STORAGE_H_
#define FFMPEG_PLAYER_INCLUDE_LOCAL_STORAGE_H_

#include <string>
#include <vector>
#include <map>
#include <functional>

#ifdef use_leveldb
#include "leveldb/db.h"
#endif

#include "utility.hpp"

template<typename T>
class LocalStorage {
public:
    LocalStorage() = default;

    ~LocalStorage() = default;

public:

    virtual bool IsOpen() { return storage.IsOpen(); }

    virtual bool Get(std::string key, std::string &ref_value) { return storage.Get(key, ref_value); }

    virtual bool Put(std::string key, std::string value) { return storage.Put(key, std::move(value)); }

    virtual bool Delete(std::string &value) { return storage.Delete(value); }

    virtual std::string BatchGet(std::vector<std::string> &ref_vec) { return storage.BatchGet(ref_vec); }

    virtual std::string BatchPut(std::map<std::string, std::string> &ref_map) { return storage.BatchPut(ref_map); }

    virtual std::string BatchDelete(std::vector<std::string> &ref_vec) { return storage.BatchDelete(ref_vec); }

private:
    T storage;
};

#ifdef use_leveldb
/**
 * @brief 线程安全,迭代器或者WriteBatch自行保证同步
 */
class LevelDB : public LocalStorage {
 private:
  leveldb::DB *db;

  bool is_open_;
 public:

  LevelDB(const std::string &path = "./data/leveldb") {
    leveldb::Options options;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, path, &db);
    is_open_ = status.ok();
  }


  ~LevelDB() {
    if (is_open_) {
      delete db;
    }
  }

  static LevelDB &Instance();

  bool IsOpen() override {
    return is_open_;
  }

  bool Get(std::string key, std::string &value) override {
    leveldb::Status s = db->Get(leveldb::ReadOptions(), key, &value);
    return s.ok();
  }

  bool Put(std::string key, std::string value) override {
    leveldb::Status s = db->Put(leveldb::WriteOptions(), key, value);
    return s.ok();
  }

  bool Delete(std::string &key) override {
    leveldb::Status s = db->Delete(leveldb::WriteOptions(), key);
    return s.ok();
  }

  std::string BatchGet(std::vector<std::string> vector) override {
    return {};
  }

  std::string BatchPut(std::map<std::string, std::string> map) override {
    return {};
  }

  std::string BatchDelete(std::vector<std::string> vector) override {
    return {};
  }

  /**
   * @code
   * 	  leveldb::WriteBatch batch;
   * 	  batch.Delete(key1);
   *      batch.Put(key2, value);
   *
   * @param batch 批量脚本
   * @return bool
   */
  bool BatchAtomic(leveldb::WriteBatch &batch){
    leveldb::Status s =  db->Write(leveldb::WriteOptions(), &batch);
    return s.ok();
  }

  /**
   * @brief 迭代器
   * @param function
   */
  void Iterator(const std::function<int(const std::string &,const  std::string &)>& function){
    leveldb::ReadOptions options;
    options.snapshot = db->GetSnapshot();
    leveldb::Iterator* iter = db->NewIterator(options);
    while (iter->Valid()){
      function(iter->key().ToString(),iter->value().ToString());
      iter->Next();
    }
    delete iter;
    db->ReleaseSnapshot(options.snapshot);
  }
};

class ldb {
public:
    template <typename... Args>
    ldb(Args&&... args) {
        auto status = leveldb::DB::Open(std::forward<Args>(args)..., &db_);
        if (!status.ok()) {
            throw std::runtime_error(status.ToString());
        }
    }

    ~ldb() {
        delete db_;
    }

    template <typename... Args>
    leveldb::Status put(leveldb::WriteOptions const& opt, Args&&... args) {
        return db_->Put(opt, std::forward<Args>(args)...);
    }

    template <typename... Args>
    leveldb::Status put(Args&&... args) {
        return db_->Put(leveldb::WriteOptions(), std::forward<Args>(args)...);
    }

    template <typename... Args>
    leveldb::Status put_sync(Args&&... args) {
        leveldb::WriteOptions wo;
        wo.sync = true;
        return db_->Put(wo, std::forward<Args>(args)...);
    }

    template <typename... Args>
    std::pair<leveldb::Status, std::string> get(leveldb::ReadOptions const& opt, Args&&... args) {
        std::string s;
        leveldb::Status ret = db_->Get(leveldb::ReadOptions(), std::forward<Args>(args)..., &s);
        return { ret, std::move(s) };
    }

    template <typename... Args>
    std::pair<leveldb::Status, std::string> get(Args&&... args) {
        std::string s;
        leveldb::Status ret = db_->Get(leveldb::ReadOptions(), std::forward<Args>(args)..., &s);
        return { ret, std::move(s) };
    }

    template <typename... Args>
    leveldb::Status del(leveldb::WriteOptions const& opt, Args&&... args) {
        return db_->Delete(opt, std::forward<Args>(args)...);
    }

    template <typename... Args>
    leveldb::Status del(Args&&... args) {
        return db_->Delete(leveldb::WriteOptions(), std::forward<Args>(args)...);
    }

    template <typename Func>
    void for_each(Func&& f) {
        std::unique_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
        if (!it) throw std::bad_alloc();

        for (it->SeekToFirst(); it->Valid(); it->Next()) {
            if (it->status().ok()) {
                std::forward<Func>(f)(it->key().ToString(), it->value().ToString());
            }
        }
    }

private:
    leveldb::DB* db_ = nullptr;
};
#endif

#endif //FFMPEG_PLAYER_INCLUDE_LOCAL_STORAGE_H_
